/*
Copyright (c) 2009, Young Digital Planet SA
All rights reserved.

* Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this 
  list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, 
  this list of conditions and the following disclaimer in the documentation 
  and/or other materials provided with the distribution.
* Neither the name of the Young Digital Planet SA nor the names of its 
  contributors may be used to endorse or promote products derived from this 
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/
package pl.ydp.utils
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	
	import flexunit.framework.Assert;
	import flexunit.framework.TestCase;
	
	public class EventExecutionChain2Test extends TestCase implements IEventDispatcher
	{
		internal static var EVENT_TYPE1:String = "$$event1$$";
		internal static var EVENT_TYPE2:String = "$$event2$$";
		
		internal var m4called:Boolean = false;
		internal var listeners:Array = [];
		internal var eventDispatcher:EventDispatcher;
		
		internal var calledFnWithEvent:Array = [];
		
		public function EventExecutionChain2Test() {
			eventDispatcher = new EventDispatcher(this);
		}
		
		internal function m1( calls:Array, sep:String, chain:EventExecutionChain2 ):void {
			calls.push( "m1" );
			calls.push( sep );
			chain.schedule( m3, calls, sep, chain );
		}
		internal function m2( calls:Array, sep:String ):void {
			calls.push( "m2" );
			calls.push( sep );
		}
		internal function m3( calls:Array, sep:String, chain:EventExecutionChain2 ):void {
			calls.push( "m3" );
			calls.push( sep );
			chain.schedule( m4 );
		}
		internal function m4():void {
			m4called = true;
		}
		
		internal function em1( chain:EventExecutionChain2 ):void {
			calledFnWithEvent.push( "em1" );
			chain.schedule( em1_sub1 );
			chain.scheduleEvent( this, EVENT_TYPE1, em1_callback );
			chain.scheduleEvent( this, EVENT_TYPE1, em2_callback, calledFnWithEvent, 'ma', 'kota' );
			chain.schedule( em1_sub2 );
		}
		
		internal function em1_callback( ev:Event ):void {
			Assert.assertEquals( "event type", EVENT_TYPE1, ev.type );
			
			calledFnWithEvent.push( "em1_callback" );
		}
		
		internal function em2_callback( ev:Event, results:Array, str2:String, str3:String
										 ):void {
			Assert.assertEquals( "checking first argument", "ma", str2 );
			Assert.assertEquals( "checking first argument", "kota", str3 );
			results.push( "em2_callback" );
		}
		
		internal function em1_sub1():void {
			calledFnWithEvent.push( "em1_sub1" );
		}
		
		internal function em1_sub2():void {
			calledFnWithEvent.push( "em1_sub2" );
		}
		
		internal function em2( ):void {
			calledFnWithEvent.push( "em2" );
		}
		
		public function testChain():void {
			var chain:EventExecutionChain2 = new EventExecutionChain2();
			Assert.assertFalse( "Chain reported waits for events", chain.waitsForEvent() );
			
			var res:Array = [];
			chain.schedule( m1, res, "+", chain );
			chain.schedule( m2, res, "-" );
			Assert.assertFalse( "Chain reported waits for events", chain.waitsForEvent() );
			chain.start();
			
			Assert.assertEquals( "Invalid execution order", "m1+m3+m2-", res.join("") );
			Assert.assertFalse( "Chain reported waits for events", chain.waitsForEvent() );
			Assert.assertTrue( "m4 function was not called", m4called );
		}
		
		public function testChainWithEvent():void {
			var chain:EventExecutionChain2 = new EventExecutionChain2();
			
			chain.schedule( em1, chain );
			chain.schedule( em2 );
			
			chain.start();
			
			Assert.assertTrue( "Chain not reported wait for events", chain.waitsForEvent() );
			Assert.assertEquals( "Invalid execution order", "em1+em1_sub1", calledFnWithEvent.join("+") );
			var ev:Event = new Event( EVENT_TYPE1 );
			dispatchEvent( ev );
			Assert.assertFalse( "Chain reported waits for events", chain.waitsForEvent() );
			Assert.assertEquals( "Invalid execution order", "em1+em1_sub1+em1_callback+em2_callback+em1_sub2+em2", calledFnWithEvent.join("+") );
		}
		
		public function testChainWithAlternateEvents1():void {
			var chain:EventExecutionChain2 = new EventExecutionChain2();
			
			chain.beginCallbackGroup();
			chain.scheduleEvent( this, EVENT_TYPE1, em1_callback );
			chain.scheduleEvent( this, EVENT_TYPE2, em2_callback, calledFnWithEvent, 'ma', 'kota' );
			chain.endCallbackGroup();
			
			Assert.assertTrue( "Chain not reported wait for events", chain.waitsForEvent() );
			
			var ev:Event = new Event( EVENT_TYPE1 );
			dispatchEvent( ev );
			Assert.assertFalse( "Chain reported waits for events", chain.waitsForEvent() );
			Assert.assertEquals( "Invalid execution order", "em1_callback", calledFnWithEvent.join("+") );
		}
		
		public function testChainWithAlternateEvents2():void {
			var chain:EventExecutionChain2 = new EventExecutionChain2();
			
			chain.beginCallbackGroup();
			chain.scheduleEvent( this, EVENT_TYPE1, em1_callback );
			chain.scheduleEvent( this, EVENT_TYPE2, em2_callback, calledFnWithEvent, 'ma', 'kota' );
			chain.endCallbackGroup();
			
			chain.start();
			
			Assert.assertTrue( "Chain not reported wait for events", chain.waitsForEvent() );
			
			var ev:Event = new Event( EVENT_TYPE2 );
			dispatchEvent( ev );
			Assert.assertFalse( "Chain reported waits for events", chain.waitsForEvent() );
			Assert.assertEquals( "Invalid execution order", "em2_callback", calledFnWithEvent.join("+") );
		}
		
		public function testChainWithAlternateCallbacks():void {
			var chain:EventExecutionChain2 = new EventExecutionChain2();
			
			var res:String = "";
			chain.beginCallbackGroup();
			var f1:Function = chain.scheduleCallback( function():void {
				res="f1";
			} );
			var f2:Function = chain.scheduleCallback( function():void {
				res="f2";
			} );
			chain.endCallbackGroup();
			chain.schedule( function():void {
				res += "+f3";
			} );
			chain.start();
			
			Assert.assertTrue( "Chain not reported wait for events", chain.waitsForEvent() );
			
			f2();
			Assert.assertFalse( "Chain reported waits for events", chain.waitsForEvent() );
			Assert.assertEquals( "Invalid execution order", "f2+f3", res );
		}
		
		public function testThrow():void {
			var chain:EventExecutionChain2 = new EventExecutionChain2();
			
			var res:String = "";
			chain.schedule( function():void {
				res+="f1";
				chain.doThrow( new Error("xerror") );
			} );
			chain.schedule( function():void {
				res+="+f2";
			} );
			chain.scheduleCatch( function(x:Error):void {
				res += "+"+x.message;
			} );
			
			chain.start();
			Assert.assertEquals( "Invalid execution order", "f1+xerror", res );
		}
		
		public function testThrow2():void {
			var chain:EventExecutionChain2 = new EventExecutionChain2();
			
			var res:String = "";
			chain.schedule( function():void {
				res+="f1";
				throw new Error("xerror");
			} );
			chain.schedule( function():void {
				res+="+f2";
			} );
			chain.scheduleCatch( function(x:Error):void {
				res += "+"+x.message;
			} );
			
			chain.start();
			Assert.assertEquals( "Invalid execution order", "f1+xerror", res );
		}
		
		// IEventDispatcher
		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void {
			eventDispatcher.addEventListener( type, listener, useCapture, priority, useWeakReference );
		}
		
		public function dispatchEvent(event:Event):Boolean {
			return eventDispatcher.dispatchEvent( event );
		}
		
		public function hasEventListener(type:String):Boolean  {
			return eventDispatcher.hasEventListener( type );
		}
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void {
			eventDispatcher.removeEventListener( type, listener, useCapture );
		}
		public function willTrigger(type:String):Boolean {
			return eventDispatcher.willTrigger( type );
		}
	}
}